package pl.shenlon.poker.game;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sourceforge.jFuzzyLogic.FIS;
import net.sourceforge.jFuzzyLogic.rule.Rule;
import pl.shenlon.poker.cards.Hand;
import pl.shenlon.poker.cards.evaluation.PotOddsEvaluator;
import pl.shenlon.poker.cards.evaluation.WinChanceEvaluator;
import pl.shenlon.poker.game.gametypes.PokerGame;

public class GameState {

	public static final GameState ANY;

	public static final List<GameState> POSSIBLE_GAME_STATES;

	private static final PotOddsEvaluator POT_ODDS_EVALUATOR;

	public static Map<Integer, String> WIN_CHANCE_TERMS;

	public static Map<Integer, String> STRENGTHEN_CHANCE_TERMS;

	private static FIS winChanceFIS, strengthenChanceFIS;

	private static Map<Integer, String> evaluateTerms(String varName, FIS fis,
			int min, int max) {
		Map<Integer, String> map = new HashMap<Integer, String>();
		for (int i = min; i <= max; i++) {
			fis.setVariable(varName, i);
			fis.evaluate();
			double maxValue = 0.0;
			String ruleName = "";
			for (Rule r : fis.getFunctionBlock(varName)
					.getFuzzyRuleBlock("No1").getRules()) {

				if (maxValue < r.getDegreeOfSupport()) {
					maxValue = r.getDegreeOfSupport();
					ruleName = r.getName();
				}
			}
			map.put(i, ruleName);
		}
		return map;
	}

	public static final String ROUND_PRE_FLOP = "PRE-FLOP",
			ROUND_FLOP = "FLOP", ROUND_TURN = "TURN", ROUND_RIVER = "RIVER";

	private static final WinChanceEvaluator WIN_CHANCE_EVALUATOR;

	static {
		POSSIBLE_GAME_STATES = new ArrayList<GameState>();

		String[] rounds = new String[]{
				ROUND_PRE_FLOP,
				ROUND_FLOP,
				ROUND_TURN,
				ROUND_RIVER};
		boolean[] bets = new boolean[]{false, true};

		winChanceFIS = FIS.load(GameState.class
				.getResourceAsStream("/data/terms/winchance.fcl"), false);
		strengthenChanceFIS = FIS
				.load(GameState.class
						.getResourceAsStream("/data/terms/strengthenchance.fcl"),
						false);
		WIN_CHANCE_TERMS = evaluateTerms("winchance", winChanceFIS, 0, 100);
		STRENGTHEN_CHANCE_TERMS = evaluateTerms("strengthenchance",
				strengthenChanceFIS, 0, 100);

		for (Rule winChanceRule : winChanceFIS.getFunctionBlock("winchance")
				.getFuzzyRuleBlock("No1").getRules()) {
			for (Rule strengthenChanceRule : strengthenChanceFIS
					.getFunctionBlock("strengthenchance")
					.getFuzzyRuleBlock("No1").getRules()) {
				for (String round : rounds) {
					for (boolean bet : bets) {
						POSSIBLE_GAME_STATES.add(new GameState(winChanceRule
								.getName(), strengthenChanceRule.getName(),
								round, bet));
					}
				}
			}
		}
		ANY = new GameState("ANY", "ANY", "ANY", true);
		WIN_CHANCE_EVALUATOR = WinChanceEvaluator.getInstance();
		POT_ODDS_EVALUATOR = new PotOddsEvaluator();
	}

	public static String getGameRound(PokerGame game) {
		switch (game.getTableCards().getVisibleCardsCount()) {
			case 3 :
				return ROUND_FLOP;
			case 4 :
				return ROUND_TURN;
			case 5 :
				return ROUND_RIVER;
			case 0 :
			default :
				return ROUND_PRE_FLOP;
		}
	}

	public static String getStrengthenChanceString(double chance) {
		return STRENGTHEN_CHANCE_TERMS.get((int) (chance * 100));
	}

	public static String getWinChanceString(double chance) {
		return WIN_CHANCE_TERMS.get((int) (chance * 100));
	}

	public static boolean isBetProfitable(double strengthenChance,
			double winChance, double bet, double pot) {
		return strengthenChance + winChance >= bet / pot;
	}

	private final boolean betProfitable;

	private final String round;

	private final String strengthenChanceTerm;

	private final String winChanceTerm;

	public GameState(PokerGame game, PokerPlayer player) {
		double winChance = WIN_CHANCE_EVALUATOR.evaluateWinningChance(
				player.getHand(), game.getTableCards());
		winChanceTerm = getWinChanceString(winChance);
		double strengthenChance = POT_ODDS_EVALUATOR.getOverallHandOdds(
				player.getHand(), game.getTableCards());
		strengthenChanceTerm = getWinChanceString(strengthenChance);
		round = getGameRound(game);
		betProfitable = isBetProfitable(strengthenChance, winChance,
				game.getBetSize(), game.getPot());
	}

	public GameState(String winChanceTerm, String strengthenChanceTerm,
			String round, boolean betProfitable) {
		this.round = round;
		this.winChanceTerm = winChanceTerm;
		this.strengthenChanceTerm = strengthenChanceTerm;
		this.betProfitable = betProfitable;
	}

	@Override
	public boolean equals(Object other) {
		if (other != null && other instanceof GameState) {
			GameState that = (GameState) other;
			if (this == that) {
				return true;
			}
			return this.round.equals(that.round)
					&& this.strengthenChanceTerm
							.equals(that.strengthenChanceTerm)
					&& this.winChanceTerm.equals(that.winChanceTerm)
					&& this.betProfitable == that.betProfitable;
		}
		return false;
	}

	public String getRound() {
		return round;
	}

	public String getStrengthenChanceTerm() {
		return strengthenChanceTerm;
	}

	public String getWinChanceTerm() {
		return winChanceTerm;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + round.hashCode();
		result = prime * result + strengthenChanceTerm.hashCode();
		result = prime * result + winChanceTerm.hashCode();
		result = prime * result + (betProfitable == false ? 0 : 1);
		return result;
	}

	public boolean isBetProfitable() {
		return betProfitable;
	}

	public static String getWinChanceString(Hand player, Hand table) {
		return getWinChanceString(WIN_CHANCE_EVALUATOR.evaluateWinningChance(
				player, table));
	}

	public static String getStrenghtenChanceString(Hand player, Hand table) {
		return getStrengthenChanceString(POT_ODDS_EVALUATOR.getOverallHandOdds(
				player, table));
	}

}